﻿using GDM.Hogar.Controles.Properties;
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace GDM.Hogar.Controles.Validador.Diseño
{
    #region FlagEditor

    public sealed partial class FlagsEditor : UITypeEditor
    {
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        public FlagsEditor()
        {
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            // Validate parameter(s).
            if (provider == null)
            {
                return value;
            }
            if (value == null)
            {
                return value;
            }

            // Validate value is a enum type with Flags attribute.
            if (!value.GetType().IsEnum)
            {
                return value;
            }
            object[] attributes = value.GetType().GetCustomAttributes(typeof(FlagsAttribute), false);
            if ((attributes == null)
               || (attributes.Length == 0))
            {
                return value;
            }

            // Show panel.
            var service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (service == null)
            {
                return value;
            }

            var control = new FlagsEditorControl(value);
            service.DropDownControl(control);

            // Create new enum value.
            var type = value.GetType();
            var newValue = Activator.CreateInstance(type);
            var field = type.GetFields(BindingFlags.Public | BindingFlags.Instance)[0];
            field.SetValue(newValue, control.EditValue);

            control.Dispose();
            return newValue;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }
    }

    #endregion FlagEditor

    #region FlagEditorPanel

    partial class FlagsEditor
    {
        private sealed class FlagsEditorControl : UserControl
        {
            private readonly object _editValue;

            private CheckBox _btnNone;

            private CheckedListBox _clbElements;

            public FlagsEditorControl(object editValue)
            {
                _editValue = editValue;

                CustomComponent();
                Initialize();
            }

            public object EditValue
            {
                get
                {
                    return _editValue;
                }
            }

            private void btnNone_Click(object sender, EventArgs e)
            {
                _btnNone.Enabled = false;
                // UnSelected all items.
                _clbElements.BeginUpdate();
                for (var i = 0; i < _clbElements.Items.Count; i++)
                {
                    _clbElements.SetItemChecked(i, false);
                }
                _clbElements.EndUpdate();
                // Set enum value to 0, means "None".
                var type = _editValue.GetType();
                var field = type.GetFields()[0];
                field.SetValue(_editValue, 0);
            }

            private void clbElements_ItemCheck(object sender, ItemCheckEventArgs e)
            {
                var name = _clbElements.Items[e.Index] as string;
                // Get item value.
                var type = _editValue.GetType();
                var itemValue = (int)type.GetField(name).GetValue(_editValue);

                // Set new value.
                if (e.NewValue == CheckState.Checked)
                {
                    // Add item value.
                    var value = (int)_editValue | itemValue;
                    // Before item selected, the value is non 0, hence, after set a value, it will
                    // equal to item value, in other words, the original value is 0.
                    if (value == itemValue)
                    {
                        _btnNone.Enabled = true;
                        _btnNone.Checked = false;
                    }

                    // A enum Public and Instance field indicates its int value.
                    var field = type.GetFields(BindingFlags.Public | BindingFlags.Instance)[0];
                    field.SetValue(_editValue, value);
                }
                else
                {
                    // Sub item value
                    var value = (int)_editValue & (~itemValue);
                    // Unselected items.
                    if (value == 0)
                    {
                        _btnNone.Enabled = false;
                        _btnNone.Checked = true;
                    }

                    // A enum Public and Instance field indicates its int value.
                    var field = type.GetFields(BindingFlags.Public | BindingFlags.Instance)[0];
                    field.SetValue(_editValue, value);
                }
            }

            private void CustomComponent()
            {
                _btnNone = new CheckBox();
                _clbElements = new CheckedListBox();

                //
                // btnNone
                //
                _btnNone.Text = Resources.NONE;
                _btnNone.Appearance = Appearance.Button;
                _btnNone.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
                _btnNone.Dock = DockStyle.Top;
                _btnNone.Click += btnNone_Click;
                //
                // clbElements
                //
                _clbElements.Dock = DockStyle.Fill;
                _clbElements.CheckOnClick = true;
                _clbElements.IntegralHeight = false;
                _clbElements.ItemCheck += clbElements_ItemCheck;
                //
                // FlagsEditorControl
                //
                Controls.Add(_clbElements);
                Controls.Add(_btnNone);
            }

            private void Initialize()
            {
                Type type = _editValue.GetType();
                // Gets all enumeration members.
                var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
                foreach (var field in fields)
                {
                    // Add all enumeration members except none.
                    var value = (int)field.GetValue(_editValue);
                    if (value == 0) continue;

                    _clbElements.Items.Add(field.Name, (value & (int)_editValue) == value);
                }
            }
        }
    }

    #endregion FlagEditorPanel
}